home *** CD-ROM | disk | FTP | other *** search
/ BCI NET 2 / BCI NET 2.iso / archives / utilities / text / less-278.lha / less-278 / src.lha / source / filename.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-03-02  |  12.8 KB  |  702 lines

  1. /*
  2.  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice in the documentation and/or other materials provided with 
  12.  *    the distribution.
  13.  *
  14.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
  15.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  17.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
  18.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  19.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
  20.  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
  21.  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
  22.  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
  23.  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 
  24.  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25.  */
  26.  
  27.  
  28. /*
  29.  * Routines to mess around with filenames (and files).
  30.  * Much of this is very OS dependent.
  31.  */
  32.  
  33. #include "less.h"
  34. #if MSOFTC
  35. #include <dos.h>
  36. #endif
  37.  
  38. extern int force_open;
  39. extern IFILE curr_ifile;
  40. extern IFILE old_ifile;
  41.  
  42. /*
  43.  * Return the full pathname of the given file in the "home directory".
  44.  */
  45.     public char *
  46. homefile(filename)
  47.     char *filename;
  48. {
  49.     register char *pathname;
  50.     register char *homedir;
  51.  
  52. #if MSOFTC
  53.     /*
  54.      * If $HOME is not defined, look for the file anywhere on search path.
  55.      */
  56.     homedir = getenv("HOME");
  57.     pathname = NULL;
  58.     if (homedir != NULL)
  59.     {
  60.         /*
  61.          * Found $HOME.
  62.          */
  63.         pathname = (char *) calloc(strlen(homedir)+strlen(filename)+2, 
  64.                     sizeof(char));
  65.         if (pathname == NULL)
  66.             return (NULL);
  67.         sprintf(pathname, "%s\\%s", homedir, filename);
  68.         if (access(pathname, 0) < 0)
  69.         {
  70.             free(pathname);
  71.             pathname = NULL;
  72.         }
  73.     }
  74.     if (pathname == NULL)
  75.     {
  76.             
  77.         pathname = (char *) calloc(_MAX_PATH, sizeof(char));
  78.         _searchenv(filename, "PATH", pathname);
  79.         if (*pathname == '\0')
  80.             
  81.         {
  82.             free(pathname);
  83.             pathname = NULL;
  84.         }
  85.     }
  86.     
  87. #else
  88. #if OS2
  89.     pathname = (char *) calloc(256, sizeof(char));
  90.     if (pathname == NULL)
  91.         return (NULL);
  92.     _searchenv(filename, "INIT", pathname);
  93.     if (pathname[0] == '\0')
  94.         _searchenv(filename, "PATH", pathname);
  95.     if (pathname[0] == '\0')
  96.     {
  97.         free(pathname);
  98.         return (NULL);
  99.     }
  100. #else
  101.     homedir = getenv("HOME");
  102.     if (homedir == NULL)
  103.         return (NULL);
  104.     pathname = (char *) calloc(strlen(homedir)+strlen(filename)+2,
  105.                 sizeof(char));
  106.     if (pathname == NULL)
  107.         return (NULL);
  108.     sprintf(pathname, "%s/%s", homedir, filename);
  109. #endif
  110. #endif
  111.     return (pathname);
  112. }
  113.  
  114. /*
  115.  * Find out where the help file is.
  116.  */
  117.     public char *
  118. find_helpfile()
  119. {
  120.     register char *helpfile;
  121.     
  122.     if ((helpfile = getenv("LESSHELP")) != NULL)
  123.         return (save(helpfile));
  124. #if MSOFTC || OS2
  125.     return (homefile(HELPFILE));
  126. #else
  127.     return (save(HELPFILE));
  128. #endif
  129. }
  130.  
  131. /*
  132.  * Expand a string, substituting any "%" with the current filename,
  133.  * and any "#" with the previous filename.
  134.  * {{ This is a lot of work just to support % and #. }}
  135.  */
  136.     public char *
  137. fexpand(s)
  138.     char *s;
  139. {
  140.     register char *fr, *to;
  141.     register int n;
  142.     register char *e;
  143.  
  144.     /*
  145.      * Make one pass to see how big a buffer we 
  146.      * need to allocate for the expanded string.
  147.      */
  148.     n = 0;
  149.     for (fr = s;  *fr != '\0';  fr++)
  150.     {
  151.         switch (*fr)
  152.         {
  153.         case '%':
  154.             n += strlen(get_filename(curr_ifile));
  155.             break;
  156.         case '#':
  157.             if (old_ifile == NULL_IFILE)
  158.             {
  159.                 error("No previous file", NULL_PARG);
  160.                 return (NULL);
  161.             }
  162.             n += strlen(get_filename(old_ifile));
  163.             break;
  164.         default:
  165.             n++;
  166.             break;
  167.         }
  168.     }
  169.  
  170.     e = (char *) ecalloc(n+1, sizeof(char));
  171.  
  172.     /*
  173.      * Now copy the string, expanding any "%" or "#".
  174.      */
  175.     to = e;
  176.     for (fr = s;  *fr != '\0';  fr++)
  177.     {
  178.         switch (*fr)
  179.         {
  180.         case '%':
  181.             strcpy(to, get_filename(curr_ifile));
  182.             to += strlen(to);
  183.             break;
  184.         case '#':
  185.             strcpy(to, get_filename(old_ifile));
  186.             to += strlen(to);
  187.             break;
  188.         default:
  189.             *to++ = *fr;
  190.             break;
  191.         }
  192.     }
  193.     *to = '\0';
  194.     return (e);
  195. }
  196.  
  197. #if TAB_COMPLETE_FILENAME
  198.  
  199. /*
  200.  * Return a blank-separated list of filenames which "complete"
  201.  * the given string.
  202.  */
  203.     public char *
  204. fcomplete(s)
  205.     char *s;
  206. {
  207.     char *fpat;
  208.     /*
  209.      * Complete the filename "s" by globbing "s*".
  210.      */
  211. #if MSOFTC
  212.     /*
  213.      * But in DOS, we have to glob "s*.*".
  214.      * But if the final component of the filename already has
  215.      * a dot in it, just do "s*".  
  216.      * (Thus, "FILE" is globbed as "FILE*.*", 
  217.      *  but "FILE.A" is globbed as "FILE.A*").
  218.      */
  219.     char *slash;
  220.     for (slash = s+strlen(s)-1;  slash > s;  slash--)
  221.         if (*slash == '/' || *slash == '\\')
  222.             break;
  223.     fpat = (char *) ecalloc(strlen(s)+4, sizeof(char));
  224.     if (strchr(slash, '.') == NULL)
  225.         sprintf(fpat, "%s*.*", s);
  226.     else
  227.         sprintf(fpat, "%s*", s);
  228. #else
  229.     fpat = (char *) ecalloc(strlen(s)+2, sizeof(char));
  230.     sprintf(fpat, "%s*", s);
  231. #endif
  232.     s = glob(fpat);
  233.     if (s != NULL && strcmp(s,fpat) == 0)
  234.     {
  235.         /*
  236.          * The filename didn't expand.
  237.          */
  238.         free(s);
  239.         s = NULL;
  240.     }
  241.     free(fpat);
  242.     return (s);
  243. }
  244. #endif
  245.  
  246. /*
  247.  * Try to determine if a file is "binary".
  248.  * This is just a guess, and we need not try too hard to make it accurate.
  249.  */
  250.     public int
  251. bin_file(f)
  252.     int f;
  253. {
  254.     int i;
  255.     int n;
  256.     unsigned char data[64];
  257.  
  258.     if (!seekable(f))
  259.         return (0);
  260.     if (lseek(f, (off_t)0, 0) == BAD_LSEEK)
  261.         return (0);
  262.     n = read(f, data, sizeof(data));
  263.     for (i = 0;  i < n;  i++)
  264.         if (binary_char(data[i]))
  265.             return (1);
  266.     return (0);
  267. }
  268.  
  269. /*
  270.  * Try to determine the size of a file by seeking to the end.
  271.  */
  272.     static POSITION
  273. seek_filesize(f)
  274.     int f;
  275. {
  276.     off_t spos;
  277.  
  278.     spos = lseek(f, (off_t)0, 2);
  279.     if (spos == BAD_LSEEK)
  280.         return (NULL_POSITION);
  281.     return ((POSITION) spos);
  282. }
  283.  
  284. #if GLOB
  285.  
  286. FILE *popen();
  287.  
  288. /*
  289.  * Read a string from a file.
  290.  * Return a pointer to the string in memory.
  291.  */
  292.     static char *
  293. readfd(fd)
  294.     FILE *fd;
  295. {
  296.     int len;
  297.     int ch;
  298.     char *buf;
  299.     char *p;
  300.     
  301.     /* 
  302.      * Make a guess about how many chars in the string
  303.      * and allocate a buffer to hold it.
  304.      */
  305.     len = 100;
  306.     buf = (char *) ecalloc(len, sizeof(char));
  307.     for (p = buf;  ;  p++)
  308.     {
  309.         if ((ch = getc(fd)) == '\n' || ch == EOF)
  310.             break;
  311.         if (p - buf >= len-1)
  312.         {
  313.             /*
  314.              * The string is too big to fit in the buffer we have.
  315.              * Allocate a new buffer, twice as big.
  316.              */
  317.             len *= 2;
  318.             *p = '\0';
  319.             p = (char *) ecalloc(len, sizeof(char));
  320.             strcpy(p, buf);
  321.             free(buf);
  322.             buf = p;
  323.             p = buf + strlen(buf);
  324.         }
  325.         *p = ch;
  326.     }
  327.     *p = '\0';
  328.     return (buf);
  329. }
  330.  
  331. /*
  332.  * Execute a shell command.
  333.  * Return a pointer to a pipe connected to the shell command's standard output.
  334.  */
  335.     static FILE *
  336. shellcmd(cmd, s1, s2)
  337.     char *cmd;
  338.     char *s1;
  339.     char *s2;
  340. {
  341.     char *scmd;
  342.     char *scmd2;
  343.     char *shell;
  344.     FILE *fd;
  345.     int len;
  346.     
  347.     len = strlen(cmd) + 
  348.         (s1 == NULL ? 0 : strlen(s1)) + 
  349.         (s2 == NULL ? 0 : strlen(s2)) + 1;
  350.     scmd = (char *) ecalloc(len, sizeof(char));
  351.     sprintf(scmd, cmd, s1, s2);
  352. #if HAVE_SHELL
  353.     shell = getenv("SHELL");
  354.     if (shell != NULL && *shell != '\0')
  355.     {
  356.         /*
  357.          * Read the output of <$SHELL -c "cmd">.
  358.          */
  359.         scmd2 = (char *) ecalloc(strlen(shell) + strlen(scmd) + 7,
  360.                     sizeof(char));
  361.         sprintf(scmd2, "%s -c \"%s\"", shell, scmd);
  362.         free(scmd);
  363.         scmd = scmd2;
  364.     }
  365. #endif
  366.     fd = popen(scmd, "r");
  367.     free(scmd);
  368.     return (fd);
  369. }
  370.  
  371. /*
  372.  * Expand a filename, doing any shell-level substitutions.
  373.  */
  374.     public char *
  375. glob(filename)
  376.     char *filename;
  377. {
  378.     char *gfilename;
  379.  
  380.     filename = fexpand(filename);
  381.     if (filename == NULL)
  382.         return (NULL);
  383. #if OS2
  384. {
  385.     char **list;
  386.     int cnt;
  387.     int length;
  388.  
  389.     list = _fnexplode(filename);
  390.     length = 0;
  391.     for (cnt = 0;  list[cnt] != NULL;  cnt++)
  392.           length += strlen(list[cnt]) + 1;
  393.     gfilename = (char *) ecalloc(length, sizeof(char));
  394.     for (cnt = 0;  list[cnt] != NULL;  cnt++)
  395.     {
  396.         strcat(gfilename, list[cnt]);
  397.           strcat(gfilename, " ");
  398.     }
  399.     _fnexplodefree(list);
  400. }
  401. #else
  402. {
  403.     FILE *fd;
  404.  
  405.     /*
  406.      * We get the shell to expand the filename for us by passing
  407.      * an "echo" command to the shell and reading its output.
  408.      */
  409.     fd = shellcmd("echo %s", filename, (char*)NULL);
  410.     if (fd == NULL)
  411.     {
  412.         /*
  413.          * Cannot create the pipe.
  414.          * Just return the original (fexpanded) filename.
  415.          */
  416.         return (filename);
  417.     }
  418.     free(filename);
  419.     gfilename = readfd(fd);
  420.     pclose(fd);
  421.     if (*gfilename == '\0')
  422.         return (NULL);
  423. }
  424. #endif
  425.     return (gfilename);
  426. }
  427.  
  428. /*
  429.  * See if we should open a "replacement file" 
  430.  * instead of the file we're about to open.
  431.  */
  432.     public char *
  433. open_altfile(filename, pf, pfd)
  434.     char *filename;
  435.     int *pf;
  436.     void **pfd;
  437. {
  438.     char *lessopen;
  439.     char *gfilename;
  440.     int returnfd = 0;
  441.     FILE *fd;
  442.     
  443.     ch_ungetchar(-1);
  444.     if ((lessopen = getenv("LESSOPEN")) == NULL)
  445.         return (NULL);
  446.     if (strcmp(filename, "-") == 0)
  447.         return (NULL);
  448.     if (*lessopen == '|')
  449.     {
  450.         /*
  451.          * If LESSOPEN starts with a |, it indicates 
  452.          * a "pipe preprocessor".
  453.          */
  454.         lessopen++;
  455.         returnfd = 1;
  456.     }
  457.     fd = shellcmd(lessopen, filename, (char*)NULL);
  458.     if (fd == NULL)
  459.     {
  460.         /*
  461.          * Cannot create the pipe.
  462.          */
  463.         return (NULL);
  464.     }
  465.     if (returnfd)
  466.     {
  467. #if HAVE_FILENO
  468.         int f;
  469.         char c;
  470.  
  471.         /*
  472.          * Read one char to see if the pipe will produce any data.
  473.          * If it does, push the char back on the pipe.
  474.          */
  475.         f = fileno(fd);
  476.         if (read(f, &c, 1) != 1)
  477.             /*
  478.              * Pipe is empty.  This means there is no alt file.
  479.              */
  480.             return (NULL);
  481.         ch_ungetchar(c);
  482.         *pfd = (void *) fd;
  483.         *pf = f;
  484.         return (save("-"));
  485. #else
  486.         error("LESSOPEN pipe is not supported", NULL_PARG);
  487.         return (NULL);
  488. #endif
  489.     }
  490.     gfilename = readfd(fd);
  491.     pclose(fd);
  492.     if (*gfilename == '\0')
  493.         /*
  494.          * Pipe is empty.  This means there is no alt file.
  495.          */
  496.         return (NULL);
  497.     return (gfilename);
  498. }
  499.  
  500. /*
  501.  * Close a replacement file.
  502.  */
  503.     public void
  504. close_altfile(altfilename, filename, pipefd)
  505.     char *altfilename;
  506.     char *filename;
  507.     void *pipefd;
  508. {
  509.     char *lessclose;
  510.     FILE *fd;
  511.     
  512.     if (pipefd != NULL)
  513.         pclose((FILE*) pipefd);
  514.     if ((lessclose = getenv("LESSCLOSE")) == NULL)
  515.              return;
  516.     fd = shellcmd(lessclose, filename, altfilename);
  517.     pclose(fd);
  518. }
  519.         
  520. #else
  521. #if MSOFTC
  522.  
  523.     public char *
  524. glob(filename)
  525.     char *filename;
  526. {
  527.     register char *gfilename;
  528.     register char *p;
  529.     register int len;
  530.     register int n;
  531.     struct find_t fnd;
  532.     char drive[_MAX_DRIVE];
  533.     char dir[_MAX_DIR];
  534.     char fname[_MAX_FNAME];
  535.     char ext[_MAX_EXT];
  536.     
  537.     filename = fexpand(filename);
  538.     if (filename == NULL)
  539.         return (NULL);
  540.     if (_dos_findfirst(filename, ~0, &fnd) != 0)
  541.         return (filename);
  542.         
  543.     _splitpath(filename, drive, dir, fname, ext);
  544.     len = 100;
  545.     gfilename = (char *) ecalloc(len, sizeof(char));
  546.     p = gfilename;
  547.     do {
  548.         n = strlen(drive) + strlen(dir) + strlen(fnd.name);
  549.         while (p - gfilename + n+2 >= len)
  550.         {
  551.             len *= 2;
  552.             *p = '\0';
  553.             p = (char *) ecalloc(len, sizeof(char));
  554.             strcpy(p, gfilename);
  555.             free(gfilename);
  556.             gfilename = p;
  557.             p = gfilename + strlen(gfilename);
  558.         }
  559.         sprintf(p, "%s%s%s", drive, dir, fnd.name);
  560.         p += n;
  561.         *p++ = ' ';
  562.     } while (_dos_findnext(&fnd) == 0);
  563.     
  564.     *--p = '\0';
  565.     return (gfilename);
  566. }
  567.     
  568.     public char *
  569. open_altfile(filename)
  570.     char *filename;
  571. {
  572.     return (NULL);
  573. }
  574.  
  575.     public void
  576. close_altfile(altfilename, filename)
  577.     char *altfilename;
  578.     char *filename;
  579. {
  580. }
  581.         
  582. #else
  583.  
  584.     public char *
  585. glob(filename)
  586.     char *filename;
  587. {
  588.     return (fexpand(filename));
  589. }
  590.  
  591.     
  592.     public char *
  593. open_altfile(filename)
  594.     char *filename;
  595. {
  596.          return (NULL);
  597. }
  598.  
  599.     public void
  600. close_altfile(altfilename, filename)
  601.     char *altfilename;
  602.     char *filename;
  603. {
  604. }
  605.         
  606. #endif
  607. #endif
  608.  
  609.  
  610. #if HAVE_STAT
  611.  
  612. #include <sys/stat.h>
  613. #ifndef S_ISDIR
  614. #define    S_ISDIR(m)    (((m) & S_IFMT) == S_IFDIR)
  615. #endif
  616. #ifndef S_ISREG
  617. #define    S_ISREG(m)    (((m) & S_IFMT) == S_IFREG)
  618. #endif
  619.  
  620. /*
  621.  * Returns NULL if the file can be opened and
  622.  * is an ordinary file, otherwise an error message
  623.  * (if it cannot be opened or is a directory, etc.)
  624.  */
  625.     public char *
  626. bad_file(filename)
  627.     char *filename;
  628. {
  629.     register char *m;
  630.     struct stat statbuf;
  631.  
  632.     if (stat(filename, &statbuf) < 0)
  633.         return (errno_message(filename));
  634.  
  635.     if (force_open)
  636.         return (NULL);
  637.  
  638.     if (S_ISDIR(statbuf.st_mode))
  639.     {
  640.         static char is_dir[] = " is a directory";
  641.         m = (char *) ecalloc(strlen(filename) + sizeof(is_dir), 
  642.             sizeof(char));
  643.         strcpy(m, filename);
  644.         strcat(m, is_dir);
  645.         return (m);
  646.     }
  647.     if (!S_ISREG(statbuf.st_mode))
  648.     {
  649.         static char not_reg[] = " is not a regular file";
  650.         m = (char *) ecalloc(strlen(filename) + sizeof(not_reg), 
  651.             sizeof(char));
  652.         strcpy(m, filename);
  653.         strcat(m, not_reg);
  654.         return (m);
  655.     }
  656.  
  657.     return (NULL);
  658. }
  659.  
  660. /*
  661.  * Return the size of a file, as cheaply as possible.
  662.  * In Unix, we can stat the file.
  663.  */
  664.     public POSITION
  665. filesize(f)
  666.     int f;
  667. {
  668.     struct stat statbuf;
  669.  
  670.     if (fstat(f, &statbuf) < 0)
  671.         /*
  672.          * Can't stat; try seeking to the end.
  673.          */
  674.         return (seek_filesize(f));
  675.  
  676.     return ((POSITION) statbuf.st_size);
  677. }
  678.  
  679. #else
  680.  
  681. /*
  682.  * If we have no way to find out, just say the file is good.
  683.  */
  684.     public char *
  685. bad_file(filename)
  686.     char *filename;
  687. {
  688.     return (NULL);
  689. }
  690.  
  691. /*
  692.  * We can find the file size by seeking.
  693.  */
  694.     public POSITION
  695. filesize(f)
  696.     int f;
  697. {
  698.     return (seek_filesize(f));
  699. }
  700.  
  701. #endif
  702.